using System;
using System.IO;
using System.Runtime.InteropServices.ComTypes;

namespace org.synthful.IO
{
    public class StreamFromIStream : Stream
    {

        // the managed istream being wrapped
        IStream _Istream;

        public StreamFromIStream(IStream istream)
        {
            if (istream != null)
            {
                _Istream = istream;
            }
            else
            {
                throw new ArgumentNullException("istream");
            }
        }

        ~StreamFromIStream()
        {
            Close();
        }

        // property to get original istream object
        public IStream UnderlyingStream
        {
            get
            {
                return _Istream;
            }
        }

        // reads a specified number of bytes from the istream object
        // into memory starting at the current seek pointer
        public unsafe override int Read(byte[] buffer, int offset, int count)
        {
            if (_Istream == null)
            {
                throw new ObjectDisposedException("_Istream");
            }

            if (offset != 0)
            {
                throw new NotSupportedException("only 0 offset is supported");
            }

            int bytesRead;
            IntPtr address = new IntPtr(&bytesRead);
            _Istream.Read(buffer, count, address);
            return bytesRead;
        }


        // writes a specified number of bytes into the istream object
        // starting at the current seek pointer
        public override void Write(byte[] buffer, int offset, int count)
        {
            if (_Istream == null)
            {
                throw new ObjectDisposedException("_Istream");
            }

            if (offset != 0)
            {
                throw new NotSupportedException("only 0 offset is supported");
            }

            _Istream.Write(buffer, count, IntPtr.Zero);
        }

        // changes the seek pointer to a new location relative to the beginning
        // of the istream, the end of the istream, or the current seek position
        public unsafe override long Seek(long offset, SeekOrigin origin)
        {
            if (_Istream == null)
            {
                throw new ObjectDisposedException("_Istream");
            }

            long position = 0;
            IntPtr address = new IntPtr(&position);
            _Istream.Seek(offset, (int)origin, address);
            return position;
        }

        public override long Length
        {
            get
            {
                if (_Istream == null)
                {
                    throw new ObjectDisposedException("_Istream");
                }

                STATSTG statstg;
                _Istream.Stat(out statstg, 1 /* STATSFLAG_NONAME*/ );
                return statstg.cbSize;
            }
        }

        public override long Position
        {
            get { return Seek(0, SeekOrigin.Current); }
            set { Seek(value, SeekOrigin.Begin); }
        }


        // changes the size of the istream object
        public override void SetLength(long value)
        {
            if (_Istream == null)
            {
                throw new ObjectDisposedException("_Istream");
            }

            _Istream.SetSize(value);
        }

        // closes (disposes) the istream
        public override void Close()
        {
            if (_Istream != null)
            {
                _Istream.Commit(0);

                // Marshal.ReleaseComObject( _Istream );
                _Istream = null;
                GC.SuppressFinalize(this);
            }
        }

        public override void Flush()
        {
            _Istream.Commit(0);
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }
    }
}
